package com.aforce.entities;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Light;
import javax.media.j3d.PointLight;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;

import com.aforce.AForce3;
import com.aforce.managers.MapManager;
import com.aforce.managers.TimeManager;
import com.sun.j3d.loaders.IncorrectFormatException;
import com.sun.j3d.loaders.ParsingErrorException;
import com.sun.j3d.loaders.objectfile.ObjectFile;

public class Ship extends AbstractFlyingEntity
{
	TransformGroup ogl_group;
	long timeLastUpdated = TimeManager.getTime();
	
	boolean aiEnabled = false;
	
	int targetVelocity = 10;
	int currentVelocity = 10; // Not yet implemented
	
	//TODO: Should not be public
	public double upDelta;
	public double turnDelta;
	
	Vector3d up = new Vector3d(0.0, 1.0, 0.0);
	
	public Ship()
	{
		super(new Point3d(), new Vector3d(0,0,1));
		
		int creaseAngle = 60;
		int flags = ObjectFile.RESIZE;
		flags |= ObjectFile.TRIANGULATE;
		flags |= ObjectFile.STRIPIFY;
		ObjectFile f = new ObjectFile(flags, (float) (creaseAngle * Math.PI / 180.0));
		
		// Create the transform group node and initialize it to the
		// identity. Enable the TRANSFORM_WRITE capability so that
		// our behavior code can modify it at runtime. Add it to the
		// root of the subgraph.
		TransformGroup objTrans = new TransformGroup();
		objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

		try { objTrans.addChild(f.load(new File("./data/Hornet-1240.obj").toURI().toURL()).getSceneGroup()); }
		catch(FileNotFoundException e) { throw new RuntimeException(e); }
		catch(ParsingErrorException e) { throw new RuntimeException(e); }
		catch(IncorrectFormatException e) { throw new RuntimeException(e); }
		catch(MalformedURLException e) { throw new RuntimeException(e); }

		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
		

		
		// TODO: This lighting sucks.  Figure out why!
		Color3f light1Color = new Color3f(1f, 1f, 1f);
		Point3f atten = new Point3f(1f, 0.0f, 0.0f);
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(0.0f, 0.0f, -1.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			((PointLight) lgt1).setAttenuation(0.3f, 0.5f, 0.0f);
			objTrans.addChild(lgt1);
		}
		/*
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(0.0f, 0.0f, 1.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(0.0f, 0.0f, 1.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(0.0f, 1.0f, 0.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(0.0f, -1.0f, 0.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(1.0f, 0.0f, 0.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		{
			Light lgt1 = new PointLight(light1Color, new Point3f(-1.0f, 0.0f, 0.0f), atten);
			lgt1.setInfluencingBounds(bounds);
			objTrans.addChild(lgt1);
		}
		*/
		
		//Color3f lightColor = new Color3f (1.0f, 0.0f, 0.0f);
		//Vector3f light1Direction = new Vector3f (0.0f, 0.0f, -1f);
		
		//BoundingSphere lightBounds = new BoundingSphere (new Point3d (0, 0.0, 5), 5.0);
		
		//DirectionalLight light1  = new DirectionalLight (lightColor, light1Direction);
    	//light1.setInfluencingBounds (lightBounds);
		//light1.setInfluencingBounds(bounds);
		
    	//objTrans.addChild (light1);

    	//AmbientLight ambientLightNode = new AmbientLight (lightColor);
    	//ambientLightNode.setInfluencingBounds (bounds);
    	//objTrans.addChild (ambientLightNode);
		

		ogl_group = objTrans;
	}
	
	public void update()
	{
		long milliseconds = TimeManager.getTime() - timeLastUpdated;
		Vector3d forward = new Vector3d(getForwardVector());
		forward.scale(milliseconds * targetVelocity * 0.000001);
		getPosition().add(forward);
		
		

		// Spin - allows you to change your turning direction
		{
			AxisAngle4d rotation = new AxisAngle4d(getForwardVector(), 2*turnDelta);
			Transform3D t = new Transform3D();
			t.set(rotation);
			t.transform(getUpVector());
			getUpVector().normalize();
		}
		
		// Rise or Drop - allows you to turn up or down relative to your current orientation
		{
			Vector3d side = new Vector3d();
			side.cross(getForwardVector(), getUpVector());
			side.normalize();
			
			AxisAngle4d rotation = new AxisAngle4d(side, 2*upDelta);
			Transform3D t = new Transform3D();
			t.set(rotation);
			t.transform(getUpVector());
			getUpVector().normalize();
			t.transform(getForwardVector());
			getForwardVector().normalize();
		}
		
		Transform3D currentTransform = new Transform3D();
		currentTransform.lookAt(new Point3d(0,0,0), new Point3d(getForwardVector()), getUpVector());
		currentTransform.invert();
		currentTransform.normalize();
		currentTransform.setTranslation(new Vector3d(getPosition()));
		getTransformGroup().setTransform(currentTransform);

		
		
		Point3d position = getPosition();
		if(position.x > MapManager.getMapSizeX()) position.x = -MapManager.getMapSizeX();
		if(position.x < -MapManager.getMapSizeX()) position.x = MapManager.getMapSizeX();
		if(position.y > MapManager.getMapSizeY()) position.y = -MapManager.getMapSizeY();
		if(position.y < -MapManager.getMapSizeY()) position.y = MapManager.getMapSizeY();
		if(position.z > MapManager.getMapSizeZ()) position.z = -MapManager.getMapSizeZ();
		if(position.z < -MapManager.getMapSizeZ()) position.z = MapManager.getMapSizeZ();
		
		
		if(aiEnabled)
		{
			turnDelta += Math.random()/100 - Math.random()/100;
			upDelta += Math.random()/100 - Math.random()/100;

			if(turnDelta > 0) turnDelta -= 0.001;
			if(turnDelta < 0) turnDelta += 0.001;
			if(upDelta > 0) upDelta -= 0.001;
			if(upDelta < 0) upDelta += 0.001;
		}
	}

	@Override
	public TransformGroup getTransformGroup()
	{
		return ogl_group;
	}
	
	public void shoot()
	{
		//TODO: This does not work yet
		System.out.println("Shoot signal received");
		Bullet b = new Bullet(Bullet.BulletColor.GREEN, getPosition(), getForwardVector());
		AForce3.getGraphicsEnvironment().getEntityManager().addEntity(b);
	}
	
	public void setVelocity(int velocity)
	{
		this.targetVelocity = velocity;
	}
	
	public int getVelocity()
	{
		return targetVelocity;
	}
	
	public Vector3d getUpVector()
	{
		return up;
	}
	
	public void enableAI()
	{
		aiEnabled = true;
	}

}
